﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Ext.h".
//-------------------------------------------------------------------

#ifndef _UXS_AL_SOUND_H
#define _UXS_AL_SOUND_H

#pragma once

#include "ALSoundEffects.h"

namespace UX_STL
{
namespace ALShell
{

	class ALSound;
	SHARED_POINTER( ALSound );


	//
	// AL Format Info
	//

	struct ALFormatInfo
	{
		static bool GetInfo(al_format::type eFmt, uint *pChannels, uint *pBitrate, bool *pCompressed);
		static bool GetFormat(uint uChannels, uint uBitrate, bool bCompressed, al_format::type &eFormat);
	};



	//
	// Sound Buffer (for 3D sound)
	//

	class UX_EXPORT ALSoundBuffer : public ALBaseObject
	{
		friend class ALSound;

	protected:
		array<ALSound *>_aSounds;
		al_format::type	_eFormat;
		ALuint			_uID;
		float			_fTime;
		float			_fVolume,
						_fPitch;
		bool			_bLooping	: 1,
						_b3D		: 1;

	protected:
		void _AddSound(ALSound *pSound);
		void _RemoveSound(ALSound *pSound);
		void _UpdateSounds();

	public:
		ALSoundBuffer();
		virtual ~ALSoundBuffer();

		ALint			Id()		const	{ return _uID; }
		float			Volume()	const	{ return _fVolume; }
		float			Pitch()		const	{ return _fPitch; }
		bool			IsLooping()	const	{ return _bLooping; }
		float			Time()		const	{ return _fTime; }
		bool			Support3D()	const	{ return _b3D; }
		al_format::type	Format()	const	{ return _eFormat; }

		bool Create(const void *pData, usize uDataSize, al_format::type eFormat, uint uFreq, float fVolume, float fPitch, bool bLooping = false);
		bool CreateSound(ALSoundPtr &pSound);
		void Destroy();
	};

	SHARED_POINTER( ALSoundBuffer );
	


	//
	// Sound Sample
	//

	class UX_EXPORT ALSound : public ALBaseObject
	{
		friend class ALSoundBuffer;

	protected:
		ALSoundBufferPtr	_pBuffer;
		ALuint				_uID;
		
	protected:
		bool _Destroy();
		void _Release();

	public:
		ALSound();
		~ALSound();

		void Play()		const;
		void Stop()		const;
		void Pause()	const;
		//void Rewind()	const;
	
		bool Create(ALuint uBuffer, float fVolume, float fPitch, bool bLooping);
		bool Create(const ALSoundBufferPtr &pBuffer);
		
		// set
		void SetMatrix(const fmat4 &sMatrix)	const;
		void SetDirection(const fvec3 &sDir)	const;
		void SetPosition(const fvec3 &sPos)		const;
		void SetVelocity(const fvec3 &sVel)		const;
		void SetVolume(float fVolume = 1.f)		const;
		void SetPitch(float fPitch = 1.f)		const;
		void SetLooped(bool bLooped = false)	const;
		void SetRelative(bool bValue = false)	const;
		void SetReferenceDistance(float fDist)	const;
		void SetMaxDistance(float fDist)		const;
		void SetRollOfFactor(float fValue)		const;
		void SetCone(float fInner, float fOuter)const;
		void SetConeOuterVolume(float fVolume)	const;
		void SetVolumeLimits(float fMin, float fMax)const;
		void SetAirAbsorption(float fFactor)	const;
		void SetPlayingPos(float fSec)			const;
		void SetPlayingPosPercent(float fValue)	const;

		// get
		bool	GetLooped()				const;
		float	GetPitch()				const;
		float	GetVolume()				const;
		fvec3	GetPosition()			const;
		fvec3	GetDirection()			const;
		fvec3	GetVelocity()			const;
		float	GetMinVolume()			const;
		float	GetMaxVolume()			const;
		bool	GetRelative()			const;
		float	GetReferenceDistance()	const;
		float	GetMaxDistance()		const;
		float	GetRollOfFactor()		const;
		fvec2	GetCone()				const;
		float	GetConeOuterVolume()	const;
		float	GetAirAbsorption()		const;
		float	GetPlayingPos()			const;
		float	GetPlayingPosPercent()	const;

		// effects & filters
		void SetAUXSlot(uint uSlot, const ALSoundEffectSlotPtr &pSlot = null, const ALSoundFilterPtr &pFilter = null) const;
		void SetFilter(const ALSoundFilterPtr &pFilter = null) const;

		ALuint					Id()		const	{ return _uID; }
		const ALSoundBufferPtr&	Buffer()	const	{ return _pBuffer; }
		al_sound_state::type	GetState()	const;
	};

	

/*
=================================================
	включает/отключает вспомогательный слот
=================================================
*/
	inline void ALSound::SetAUXSlot(uint uSlot, const ALSoundEffectSlotPtr &pSlot, const ALSoundFilterPtr &pFilter) const
	{
		AL_CALL( alSource3i( _uID, AL_AUXILIARY_SEND_FILTER,
							 pSlot   != null ? pSlot->Id()   : AL_EFFECTSLOT_null, uSlot,
							 pFilter != null ? pFilter->Id() : AL_FILTER_null ) );
	}
	
/*
=================================================
	устанавливает фильтр
=================================================
*/
	inline void ALSound::SetFilter(const ALSoundFilterPtr &pFilter) const
	{
		AL_CALL( alSourcei( _uID, AL_DIRECT_FILTER, pFilter != null ? pFilter->Id() : AL_FILTER_null ) );
	}
	
/*
=================================================
	устанавливает положение и направление источника звука
	(только для 3D звука)
=================================================
*/
	inline void ALSound::SetMatrix(const fmat4 &sMatrix) const
	{
		fquat	q( sMatrix );
		SetDirection( q.GetDirection() );
		SetPosition( sMatrix.Translation().xyz() );
	}

/*
=================================================
	устанавливает направление источника звука
	(только для 3D звука)
=================================================
*/
	inline void ALSound::SetDirection(const fvec3 &sDir) const
	{
		AL_CALL( alSourcefv( _uID, AL_DIRECTION, (const ALfloat *)sDir.ptr() ) );
	}

/*
=================================================
	устанавливает позицию источника звука
	(только для 3D звука)
=================================================
*/
	inline void ALSound::SetPosition(const fvec3 &sPos) const
	{
		AL_CALL( alSourcefv( _uID, AL_POSITION, (const ALfloat *)sPos.ptr() ) );
	}
	
/*
=================================================
	устанавливает скорость источника звука
	(только для 3D звука)
=================================================
*/
	inline void ALSound::SetVelocity(const fvec3 &sVel) const
	{
		AL_CALL( alSourcefv( _uID, AL_VELOCITY, (const ALfloat *)sVel.ptr() ) );
	}
	
/*
=================================================
	устанавливает коэффициент усиление звука
	в зависимости от расстояния
=================================================
*/
	inline void ALSound::SetVolume(float fVolume) const
	{
		AL_CALL( alSourcef( _uID, AL_GAIN, fVolume ) );
	}
	
/*
=================================================
	установить тон звука
=================================================
*/
	inline void ALSound::SetPitch(float fPitch) const
	{
		AL_CALL( alSourcef( _uID, AL_PITCH, fPitch ) );
	}
	
/*
=================================================
	устанавливает зацикливание звука
=================================================
*/
	inline void ALSound::SetLooped(bool bLooped) const
	{
		AL_CALL( alSourcei( _uID, AL_LOOPING, bLooped ) );
	}

/*
=================================================
	установить пределы для затухания (громкости) звука
	(только для 3D звука)
=================================================
*/
	inline void ALSound::SetVolumeLimits(float fMin, float fMax) const
	{
		// value must clamped 0..1
		AL_CALL( alSourcef( _uID, AL_MIN_GAIN, fMin ) );
		AL_CALL( alSourcef( _uID, AL_MAX_GAIN, fMax ) );
	}
	
/*
=================================================
	устанавливает должны ли параметры источника
	интерпретироваться относительно слушателя
	(только для 3D звука)
=================================================
*/
	inline void ALSound::SetRelative(bool bValue) const
	{
		AL_CALL( alSourcei( _uID, AL_SOURCE_RELATIVE, bValue ) );
	}
	
/*
=================================================
	устанавливает расстояние меньше которого
	громкость звука не будет изменяться
	(только для 3D звука)
=================================================
*/
	inline void ALSound::SetReferenceDistance(float fDist) const
	{
		AL_CALL( alSourcef( _uID, AL_REFERENCE_DISTANCE, fDist ) );
	}
	
/*
=================================================
	установить максимальную дистанцию
	(только для 3D звука)
=================================================
*/
	inline void ALSound::SetMaxDistance(float fDist) const
	{
		AL_CALL( alSourcef( _uID, AL_MAX_DISTANCE, fDist ) );
	}

/*
=================================================
	влияет на затухание звука с растоянием
	(только для 3D звука)
=================================================
*/
	inline void ALSound::SetRollOfFactor(float fValue) const
	{
		AL_CALL( alSourcef( _uID, AL_ROLLOFF_FACTOR, fValue ) );
	}

/*
=================================================
	установить конус распространения звука
	(только для 3D звука)
=================================================
*/
	inline void ALSound::SetCone(float fInner, float fOuter) const
	{
		AL_CALL( alSourcef( _uID, AL_CONE_INNER_ANGLE, fInner ) );
		AL_CALL( alSourcef( _uID, AL_CONE_OUTER_ANGLE, fOuter ) );
	}
	
/*
=================================================
	установить затухание звука вне конуса
	(только для 3D звука)
=================================================
*/
	inline void ALSound::SetConeOuterVolume(float fVolume) const
	{
		AL_CALL( alSourcef( _uID, AL_CONE_OUTER_GAIN, fVolume ) );
	}
	
/*
=================================================
	установить коэффициент поглощения
	(только для 3D звука)
=================================================
*/
	inline void ALSound::SetAirAbsorption(float fFactor) const
	{
		AL_CALL( alSourcef( _uID, AL_AIR_ABSORPTION_FACTOR, fFactor ) );
	}
	
/*
=================================================
	установить текущую позицию воспроизведения
	звука в секундах
=================================================
*/
	inline void ALSound::SetPlayingPos(float fSec) const
	{
		AL_CALL( alSourcef( _uID, AL_SEC_OFFSET, fSec ) );
	}
	
/*
=================================================
	установить текущую позицию воспроизведения
	звука в процентах
=================================================
*/
	inline void ALSound::SetPlayingPosPercent(float fValue) const
	{
		SetPlayingPos( _pBuffer->Time() * fValue );
	}

/*
=================================================
	воспроизвести звук
=================================================
*/
	inline void ALSound::Play() const
	{
		AL_CALL( alSourcePlay( _uID ) );
	}

/*
=================================================
	остановить воспроизведение и
	вернуться к началу звука
=================================================
*/
	inline void ALSound::Stop() const
	{
		AL_CALL( alSourceStop( _uID ) );
	}

/*
=================================================
	остановить воспроизведение
=================================================
*/
	inline void ALSound::Pause() const
	{
		AL_CALL( alSourcePause( _uID ) );
	}
	
/*
=================================================
	возвращает состояние воспроизведения звука
=================================================
*/
	inline al_sound_state::type ALSound::GetState() const
	{
		ALint	i_state = 0;
		AL_CALL( alGetSourcei( _uID, AL_SOURCE_STATE, &i_state ) );
		return (al_sound_state::type)i_state;
	}

/*
=================================================
	возвращает состояние зацикливания звука
=================================================
*/
	inline bool ALSound::GetLooped() const
	{
		ALint	i_looping = 0;
		AL_CALL( alGetSourcei( _uID, AL_LOOPING, &i_looping ) );
		return !!i_looping;
	}

/*
=================================================
	возвращает тон звука
=================================================
*/
	inline float ALSound::GetPitch() const
	{
		float	f_pitch;
		AL_CALL( alGetSourcef( _uID, AL_PITCH, &f_pitch ) );
		return f_pitch;
	}
	
/*
=================================================
	возвращает затухание(громкость) звука
=================================================
*/
	inline float ALSound::GetVolume() const
	{
		float	f_Volume;
		AL_CALL( alGetSourcef( _uID, AL_GAIN, &f_Volume ) );
		return f_Volume;
	}
	
/*
=================================================
	возвращает позицию источника звука
=================================================
*/
	inline fvec3 ALSound::GetPosition() const
	{
		fvec3	v_pos;
		AL_CALL( alGetSourcefv( _uID, AL_POSITION, (ALfloat *)v_pos.ptr() ) );
		return v_pos;
	}

/*
=================================================
	возвращает направление на источник звука
=================================================
*/
	inline fvec3 ALSound::GetDirection() const
	{
		fvec3	v_dir;
		AL_CALL( alGetSourcefv( _uID, AL_DIRECTION, (ALfloat *)v_dir.ptr() ) );
		return v_dir;
	}

/*
=================================================
	возвращает скорость источника звука
=================================================
*/
	inline fvec3 ALSound::GetVelocity() const
	{
		fvec3	v_vel;
		AL_CALL( alGetSourcefv( _uID, AL_VELOCITY, (ALfloat *)v_vel.ptr() ) );
		return v_vel;
	}

/*
=================================================
	возвращает пределы затухания звука
=================================================
*/
	inline float ALSound::GetMinVolume() const
	{
		float	f_Volume;
		AL_CALL( alGetSourcef( _uID, AL_MIN_GAIN, &f_Volume ) );
		return f_Volume;
	}

	inline float ALSound::GetMaxVolume() const
	{
		float	f_Volume;
		AL_CALL( alGetSourcef( _uID, AL_MAX_GAIN, &f_Volume ) );
		return f_Volume;
	}
	
/*
=================================================
	должны ли параметры источника
	интерпретироваться относительно слушателя
=================================================
*/
	inline bool ALSound::GetRelative() const
	{
		ALint	i_val;
		AL_CALL( alGetSourcei( _uID, AL_SOURCE_RELATIVE, &i_val ) );
		return !!i_val;
	}
	
/*
=================================================
	возвращает расстояние меньше которого
	громкость звука не будет изменяться
=================================================
*/
	inline float ALSound::GetReferenceDistance() const
	{
		float	f_val;
		AL_CALL( alGetSourcef( _uID, AL_REFERENCE_DISTANCE, &f_val ) );
		return f_val;
	}
	
/*
=================================================
	возвращает максимальную дистанцию
=================================================
*/
	inline float ALSound::GetMaxDistance() const
	{
		float	f_val;
		AL_CALL( alGetSourcef( _uID, AL_MAX_DISTANCE, &f_val ) );
		return f_val;
	}

/*
=================================================
	возвращает зависимость затухания звука от расстояния
=================================================
*/
	inline float ALSound::GetRollOfFactor() const
	{
		float	f_val;
		AL_CALL( alGetSourcef( _uID, AL_ROLLOFF_FACTOR, &f_val ) );
		return f_val;
	}

/*
=================================================
	возвращает параметры конуса распространения звука
=================================================
*/
	inline fvec2 ALSound::GetCone() const
	{
		fvec2	v_val;
		AL_CALL( alGetSourcef( _uID, AL_CONE_INNER_ANGLE, (ALfloat *)&v_val.x ) );
		AL_CALL( alGetSourcef( _uID, AL_CONE_OUTER_ANGLE, (ALfloat *)&v_val.y ) );
		return v_val;
	}

/*
=================================================
	возвращает затухание звука вне конуса
=================================================
*/
	inline float ALSound::GetConeOuterVolume() const
	{
		float	f_val;
		AL_CALL( alGetSourcef( _uID, AL_CONE_OUTER_GAIN, &f_val ) );
		return f_val;
	}
	
/*
=================================================
	возвращает коэффициент поглощения
=================================================
*/
	inline float ALSound::GetAirAbsorption() const
	{
		float	f_val;
		AL_CALL( alGetSourcef( _uID, AL_AIR_ABSORPTION_FACTOR, &f_val ) );
		return f_val;
	}
	
/*
=================================================
	возвращает текущую позицию воспроизведения
	звука в секундах
=================================================
*/
	inline float ALSound::GetPlayingPos() const
	{
		float	f_val;
		AL_CALL( alGetSourcef( _uID, AL_SEC_OFFSET, &f_val ) );
		return f_val;
	}
	
/*
=================================================
	возвращает текущую позицию воспроизведения
	звука в процентах
=================================================
*/
	inline float ALSound::GetPlayingPosPercent() const
	{
		return GetPlayingPos() / _pBuffer->Time();
	}

//-------------------------------------------------------------------

}	// ALShell
}	// UX_STL

#endif	// _UXS_AL_SOUND_H